#pragma once

#include <pcrecpp.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/lexical_cast.hpp>

#include "cdb.h"
#include "fcntl.h"

#include "henry.hpp"
#include "inference_engine.hpp"

using namespace std;
using namespace boost;

/* Private cdb functions. */
struct cdb_t {
  cdb  db;
  int  fd;
  bool is_loaded;
  cdb_t() { is_loaded = false; }
};

bool _cdb_load( cdb_t *p_cdb, const string &filename );
void _cdb_unload( cdb_t &db );
bool _cdb_haskey( cdb_t &db, const string &key );
bool _cdb_get( cdb_t &db, string *p_out, const string &key );

namespace kb {

  struct pps_template_t {
    pcrecpp::RE   regex;    
    string        predicate, property, regex_str;
    int           arity;
    pps_template_t( string &_predicate, int _arity, string &_property ) :
      regex( "("+_predicate+")\\(.*" ), predicate( _predicate ), property( _property ), arity( _arity ) {};
    pps_template_t( const string &line ) : regex( "" ), property( line ) {
      static pcrecpp::RE regex_split( "/(.*?)/(\\d):\\d" );
      regex_split.PartialMatch( line, &predicate, &arity );
      regex = pcrecpp::RE( "("+predicate+"/"+lexical_cast<string>(arity)+")" );
    };
    pps_template_t() : regex( "" ) {};
  };
  
  class pool_t {

  private:
    cdb_t         m_cdb_axioms;
    ss_dict_t     m_axiom_no;
    int           m_axiom_counter;
    bool          m_f_rhs_strict;

  public:
    ~pool_t();

    inline bool is_conditioned( const string& predicate ) { string q = "$PP$"+predicate; return _cdb_haskey( m_cdb_axioms, q ); };
    inline bool get_conditioned_property( string_list_t *p_out_props, const string& predicate ) { string q = "$PP$"+predicate, out; if( _cdb_get( m_cdb_axioms, &out, q ) ) { string_list_t sl_out; split( sl_out, out, is_any_of("\t") ); p_out_props->insert( p_out_props->end(), sl_out.begin(), sl_out.end() ); } };
    inline bool get_disjoint_id( int_list_t *p_out, const string& predicate ) { string q = "$DJ$"+predicate, out; bool ret = _cdb_get( m_cdb_axioms, &out, q ); if( !ret ) { return false; } string_list_t sl_ids; split( sl_ids, out, is_any_of("\t") ); foreach( string_list_t, iter_id, sl_ids ) { p_out->push_back( lexical_cast<int>( *iter_id ) ); } return true; };
    inline string get_disjoint_prop( int djid ) { string q = "$DJ-"+lexical_cast<string>(djid)+"$", out; return _cdb_get( m_cdb_axioms, &out, q ) ? out : ""; };

    inline string_list_t get_options() { string out; string_list_t ret; _cdb_get( m_cdb_axioms, &out, "$OPTIONS$" ); split( ret, out, is_any_of( "\t" ) ); return ret; }
    inline string_list_t get_regex_cups() { string out; string_list_t ret; _cdb_get( m_cdb_axioms, &out, "$PP-REGEX$" ); split( ret, out, is_any_of( "\t" ) ); return ret; }
    inline string_set_t get_ignore_ms() { string out; string_set_t ret; _cdb_get( m_cdb_axioms, &out, "$IGNORE-M$" ); split( ret, out, is_any_of( "\t" ) ); return ret; }
    
    inline ss_dict_t &get_axiom_dict() { return m_axiom_no; };

    inline void initialize_axiom_log() { m_axiom_no.clear(); m_axiom_counter = 0; };

    bool load_axioms( const string& );

    void extract_applicable_axioms( sss_dict_t*, const literal_vector_t& );
    bool apply_applicable_axioms( const sss_dict_t&, bool (*)(const literal_list_t&, void*), void*, const slps_dict_t&, literal_vector_t&, int, bool, bool );
    
    void reasoning( vector<literal_list_t>*, const literal_t&, const slps_dict_t&, int, bool );

    inline axiom_t register_applied_axiom( const string &the_aa_key ) { return std::tr1::hash<string>()( the_aa_key ); }
    
  };

  bool compile( const string&, istream& );
  bool parse_literal( literal_t*, const string&, vv_dict_t* = NULL, bool = false, const string& = "" );
  bool match_arguments( vv_dict_t*, const literal_t&, const logvar_list_t& );
  
}

